package net.danielthompson.danray;

import net.danielthompson.danray.runners.AdaptivePixelRunner;
import net.danielthompson.danray.runners.PixelRunner;
import net.danielthompson.danray.structures.BitMatrix;
import net.danielthompson.danray.structures.Scene;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

/**
 * Created by daniel on 3/4/14.
 */
public class TraceManager {
   BufferedImage _image;
   Canvas _canvas;
   Graphics _graphics;

   BufferedImage _countImage;
   Canvas _countCanvas;
   Graphics _countGraphics;

   private final int _maxDepth;
   private final int _samplesPerPixel;
   private final int _superSamplesPerPixel;
   private final float _samplesInverse;
   private final int _airIndexOfRefraction;

   int _x;
   int _y;
   int _threads;

   Scene _scene;

   Tracer _tracer;

   int _xPointer;
   int _yPointer;

   BitMatrix _matrix;

   /**
    *
    * @param threads Number of threads to spawn.
    * @param image The image to render to.
    * @param canvas The canvas to display the image on.
    * @param scene The scene to be rendered.
    * @param x The width of the image in pixels.
    * @param y The height of the image in pixels.
    * @param maxDepth The maximum number of times a ray can be refracted or reflected.
    * @param samplesPerPixel How many samples to take per pixel.
    * @param airIndexOfRefraction The index of refraction of air.
    */
   public TraceManager(int threads, BufferedImage image, Canvas canvas, BufferedImage countImage, Canvas countCanvas, Scene scene, int x, int y, int maxDepth, int samplesPerPixel, int superSamplesPerPixel, int airIndexOfRefraction) {
      _threads = threads;
      _image = image;
      _canvas = canvas;
      _maxDepth = maxDepth;
      _samplesPerPixel = samplesPerPixel;
      _superSamplesPerPixel = superSamplesPerPixel;
      _samplesInverse = 1.0f / (_superSamplesPerPixel * _samplesPerPixel);
      _airIndexOfRefraction = airIndexOfRefraction;
      _graphics = canvas.getGraphics();
      _countGraphics = countCanvas.getGraphics();

      _x = x;
      _y = y;
      _scene = scene;

      _countCanvas = countCanvas;
      _countImage = countImage;

      _tracer = new Tracer(_scene, _maxDepth);

      _xPointer = 0;
      _yPointer = 0;


      for (int i = 0; i < x; ++i)
         for (int j = 0; j < y; ++j)
            _image.setRGB(i, j, Color.darkGray.getRGB()); // Black Background

      _matrix = new BitMatrix(x, y);
   }

   public void Trace() {

      Runnable runner = new PixelRunner(this, _tracer, _scene, _x, _y, _samplesPerPixel, _superSamplesPerPixel);

      if (_threads <= 1) {
         runner.run();
      }
      else {
         java.util.List<Thread> threads = new ArrayList<Thread>(_threads);

         for (int i = 0; i < _threads; i++) {

            threads.add(new Thread(runner));
            threads.get(i).start();
         }

         for (Thread thread : threads) {
            try {
               thread.join();
            }
            catch (InterruptedException e) {
               System.out.println("Caught interrupted exception.");
            }
         }
      }
      Main.Finished = true;
   }

   public void Trace(int[] pixel) {
      PixelRunner runner = new PixelRunner(this, _tracer, _scene, _x, _y, _samplesPerPixel, _superSamplesPerPixel);
      runner.trace(pixel);
   }

   public void UpdateCanvas() {
      _canvas.update(_graphics);
      _countCanvas.update(_countGraphics);
   }

   public void SetPixelColor(int[] pixel, Color color) {
      _image.setRGB(pixel[0], pixel[1], color.getRGB());
   }

   public void SetPixelColor(int x, int y, Color color) throws Exception {

      _image.setRGB(x, y, color.getRGB());

   }

   public void SetRayCountForPixel(int[] pixel, int count) {

      float normalizedColor = count * _samplesInverse;
      int expandedColor = (int)(255.0f * normalizedColor);

      Color color = new Color(expandedColor, expandedColor, expandedColor);
      _countImage.setRGB(pixel[0], pixel[1], color.getRGB());
   }
}
